﻿using System;
using System.Collections;
using System.Collections.Generic;
using Cosmos.Assembler.x86;
using DimOS.Constants;
using DimOS.DimSH;
using DimOS.Internals;
using DimOS.Plugs;
using String = DimOS.Plugs.String;
using Convert = DimOS.Plugs.Convert;
using DateTime = DimOS.Plugs.DateTime;
using SH = DimOS.DimSH.Shell;

namespace DimOS.DimFS
{
    public static class Path
    {
        private static List<string> Normalize(string path, bool includeSeparators, bool includeValues = true)
        {
            string normalized = Separators.IndicatorRoot.ToString();

            bool triggerValue = false;
            bool triggerString = false;
            bool trigger = false;

            if (path[0] == Separators.IndicatorUser) path = path.Remove(0, 1).Insert(0, Layout.User);
            else if (path[0] == Separators.PathTemp) path = path.Remove(0, 1).Insert(0, Layout.Temp);
            else if (path[0] == Separators.PathCurrent) path = path.Remove(0, 1).Insert
                (0, SH.WorkingDirectory.Substring(0, SH.WorkingDirectory.Length - 1));
            else if (path[0] == Separators.PathDelimiter) path = path.Remove(0, 1).Insert(0, SH.WorkingDirectory);
            else if (path[0] == Separators.PathParent) path = path.Insert(0, SH.WorkingDirectory);

            for (int i = 1; i < path.Length; i++)
            {
                if (path[i] == Tokens.ValueLeft && !triggerValue) triggerValue = trigger = true;
                if (path[i] == Tokens.VerbatimString && triggerValue && (!triggerString ||
                (path[i - 1] != Tokens.MirrorLeft && path[i - 1] != Tokens.MirrorRight)))
                { triggerString = !triggerString; trigger = true; }

                if (path[i] == Tokens.ValueRight && triggerValue)
                {
                    if (triggerString)
                    {
                        triggerString = false;
                        normalized += Tokens.VerbatimString;
                    }
                    triggerValue = false; trigger = true;
                }
                
                if (!triggerString && !trigger)
                {
                    if (path[i] != Separators.IndicatorRoot && path[i] != Separators.IndicatorUser && path[i] != Tokens.End &&
                        path[i] != Separators.PathTemp && path[i] != Separators.SpaceSplitter && path[i] != Tokens.VerbatimString &&
                        path[i] != Tokens.ValueLeft && !((path[i] == Tokens.WildcardAny || path[i] == Tokens.WildcardOne) && triggerValue) &&
                        path[i] != Tokens.Convert && path[i] != Tokens.ValueRight && !(path[i] == Tokens.Index && !triggerValue) &&
                        path[i] != Tokens.MirrorLeft && path[i] != Tokens.MirrorRight && path[i] != Separators.PathCurrent &&
                        path[i] != Tokens.CommandLeft && path[i] != Tokens.CommandRight && path[i] != Tokens.ParameterLeft &&
                        path[i] != Tokens.ParameterRight) normalized += path[i];  
                }
                else normalized += path[i];

                trigger = false;
            }
            if (triggerString) normalized += Tokens.VerbatimString;
            if (triggerValue) normalized += Tokens.ValueRight;
            triggerValue = triggerString = false;
            path = Separators.IndicatorRoot.ToString();

            for (int i = 1; i < normalized.Length; i++)
            {
                if (normalized[i] == Tokens.ValueLeft) triggerValue = true;
                if (normalized[i] == Tokens.ValueRight) triggerValue = trigger = false;
                if (normalized[i] == Tokens.VerbatimString && normalized[i - 1] != Tokens.MirrorLeft 
                    && normalized[i - 1] != Tokens.MirrorRight) triggerString = !triggerString;

                if (!triggerString)
                {
                    if (!(normalized[i] == normalized[i - 1] && (normalized[i] == Separators.PathDelimiter
                        || normalized[i] == Separators.PathParent || normalized[i] == Tokens.Index ||
                        normalized[i] == Tokens.WildcardAny)) &&
                        !((normalized[i] == Tokens.Index || normalized[i] == Separators.PathDelimiter
                        || normalized[i] == Separators.PathParent) && trigger)) path += normalized[i];
                }
                else path += normalized[i];

                if ((normalized[i] == Tokens.Index || normalized[i] == Separators.PathDelimiter
                    || normalized[i] == Separators.PathParent) && triggerValue) trigger = true;
            }
            path = String.Replace(path, Tokens.VerbatimString + "" + Tokens.VerbatimString, "", true);
            trigger = false;

            if (path[path.Length - 1] != Separators.PathDelimiter)
            {
                triggerString = true;
                path += Separators.PathDelimiter;
            }

            int j = 0;
            List<string> nodes = new List<string>();

            for (int i = 0; i < path.Length; i++)
            {
                if (path[i] == Tokens.ValueLeft) triggerValue = true;
                if (path[i] == Tokens.ValueRight) triggerValue = false;

                if (path[i] != Separators.PathDelimiter || triggerValue) continue;

                var chunk = path.Substring(j, i - j);
                if (chunk.Length != 0)
                {
                    var value = chunk.IndexOf(Tokens.ValueLeft);
                    if (value != -1)
                    {
                        var temp1 = chunk.Substring(0, value);
                        var temp2 = String.Replace(temp1, Separators.PathParent.ToString(), "");
                        var temp3 = includeValues ? chunk.Substring(value, chunk.IndexOf(Tokens.ValueRight) + 1) : "";

                        if (temp1 == Separators.PathParent.ToString() && j != 1) chunk = Separators.PathParent.ToString();
                        else if (j == 0) chunk = Separators.IndicatorRoot + temp3;
                        else chunk = temp1 != "" ? temp2 + temp3 : "";
                    }
                    if (chunk == Separators.PathParent.ToString())
                    {
                        if (trigger) nodes.RemoveAt(nodes.Count - 1);
                    }
                    else
                    {
                        trigger = j != 0;
                        if (j == 0 && value == -1) chunk = Separators.IndicatorRoot.ToString();
                        nodes.Add((value == -1 ? String.Replace(chunk, Separators.PathParent + "", "")
                            : chunk) + (includeSeparators ? Separators.PathDelimiter.ToString() : ""));
                    }
                }
                j = i + 1;
            }
            if (triggerString && includeSeparators) nodes[nodes.Count - 1] =
                nodes[nodes.Count - 1].Remove(nodes[nodes.Count - 1].Length - 1);
            return nodes;
        }

        public static string Normalize(string path)
        {
            var list = Normalize(path, true, Settings.AdvancedPaths); path = "";
            for (int i = 0; i < list.Count; i++) path += list[i];
            return path;
        }

        public static bool Normalized(string path)
        { return path != Normalize(path); }

        public static bool Valid(string path)
        {
            return path != "" && (path[0] == Separators.PathDelimiter || path[0] == Separators
            .PathCurrent || path[0] == Separators.PathParent || path[0] == Separators.IndicatorRoot
            || path[0] == Separators.IndicatorUser || path[0] == Separators.PathTemp);
        }

        //public static bool Exists(string path) { return Evaluate(Normalize(path, false, false), 1, Layout.RootNode).Count != 0; }

        private static List<string> ReturnNames(List<string> chunks)
        {
            var result = new List<string>();
            for (int i = 0; i < chunks.Count; i++)
            {
                var index = chunks[i].IndexOf(Tokens.ValueLeft);
                result.Add(index == -1 ? chunks[i] : chunks[i].Substring(0, index));
            }
            return result;
        }

        private static List<string> ReturnValues(List<string> chunks)
        {
            var result = new List<string>();
            for (int i = 0; i < chunks.Count; i++)
            {
                var index = chunks[i].IndexOf(Tokens.ValueLeft);
                result.Add(index == -1 ? "" : chunks[i].Substring(index, chunks[i].Length - index));
            }
            return result;
        }

        internal static List<Node> Resolve(string path, Permissions permissions, out Error error, ref List<string> nonTraversable,
            ref List<string> insufficientPrivileges, ref List<string> notCompleted, ref List<string> created, out string newName,
            out bool delimited, bool createNew = false, bool overwrite = false, bool rename = false, bool includeHidden = false)
        {
            error = Error.None;
            var chunks = Normalize(path, true, Settings.AdvancedPaths);
            delimited = chunks[chunks.Count - 1][chunks[chunks.Count - 1].Length - 1] == Separators.PathDelimiter;
            if (delimited) chunks[chunks.Count - 1] = chunks[chunks.Count - 1].Remove(chunks[chunks.Count - 1].Length - 1);
            for (int index = 0; index < chunks.Count - 1; index++) chunks[index] = chunks[index].Remove(chunks[index].Length - 1);

            var names = Settings.AdvancedPaths ? ReturnNames(chunks) : chunks;
            if (rename && !delimited)
            {
                newName = String.Replace(String.Replace(names[names.Count - 1],
                Tokens.WildcardAny.ToString(), ""),Tokens.WildcardOne.ToString(), "");

                if (newName == "") { error = Error.InvalidName; return new List<Node>(); }
                chunks.RemoveAt(chunks.Count - 1); delimited = true;
                names = Settings.AdvancedPaths ? ReturnNames(chunks) : chunks;
            }
            else newName = "";

            var values = Settings.AdvancedPaths ? ReturnValues(chunks) : new List<string>();
            var matches = new List<NodeLevel>{new NodeLevel(0, Layout.RootNode)};
            bool exists = names.Count == 1 || Evaluate(names, 1, Layout.RootNode, ref matches, false, includeHidden).Count != 0;
            
            var nodes = new List<Node>();

            int topLevel = 0;
            for (int index = 0; index < matches.Count; index++)
                if (matches[index].Level > topLevel) topLevel = matches[index].Level;

            if (exists && overwrite)
            {
                for (int i = 0; i < matches.Count; i++)
                    if (matches[i].Level == topLevel && String.Replace(String.Replace(names[topLevel],
                        Tokens.WildcardAny.ToString(), ""), Tokens.WildcardOne.ToString(), "") != "" &&
                        (Settings.AdvancedPaths || ((!delimited && matches[i].Node.Content.Children == null) ||
                        (delimited && matches[i].Node.Content.Children != null)))) matches[i].Node.Destroy();
                matches = new List<NodeLevel> { new NodeLevel(0, Layout.RootNode) };
                exists = names.Count == 1 || Evaluate(names, 1, Layout.RootNode, ref matches, false, includeHidden).Count != 0;
                topLevel = 0;
                for (int index = 0; index < matches.Count; index++)
                    if (matches[index].Level > topLevel) topLevel = matches[index].Level;
            }

            if (!Settings.AdvancedPaths)
            {
                var traversable = new List<NodeLevel>();
                for (int i = 0; i < matches.Count; i++)
                {
                    if (matches[i].Node.Content.Children == null &&
                        !(matches[i].Level == names.Count - 1 && !delimited))
                    nonTraversable.Add(Get(matches[i].Node, false));
                    else traversable.Add(matches[i]);
                }
                if (nonTraversable.Count != 0 ) error = Error.NotTraversable;
                if (traversable.Count == 0) return new List<Node>();
                matches = traversable;
            }

            if (!exists && !createNew) { error = Error.NotFound; return new List<Node>(); }

            var permitted = new List<NodeLevel>();
            for (int i = 0; i < matches.Count; i++)
            {
                bool write = true;

                if (permissions.Owner != Account.Root && values.Count != 0 && values[matches[i].Level] != ""
                    && (!matches[i].Node.Permissions.Write || !matches[i].Node.Permissions.Read))
                { insufficientPrivileges.Add(Get(matches[i].Node, false)); write = false; }

                if (exists && matches[i].Level == names.Count - 1
                    && (!(!Settings.AdvancedPaths && matches[i].Node.Content.Children == null && delimited)
                    && !(!Settings.AdvancedPaths && matches[i].Node.Content.Children != null && !delimited))
                    && (!Permissions.Compare(permissions, matches[i].Node.Permissions) 
                    || (matches[i].Node.Attributes.System && permissions.Delete)))
                { insufficientPrivileges.Add(Get(matches[i].Node, delimited)); write = false; }
                    
                if (permissions.Owner != Account.Root && !exists && matches[i].Level == topLevel
                    && !matches[i].Node.Permissions.Write)
                { insufficientPrivileges.Add(Get(matches[i].Node)); write = false; }

                if (write) permitted.Add(matches[i]);
            }
            if (insufficientPrivileges.Count != 0) error = Error.InsufficientPrivileges;
            if (permitted.Count == 0) return new List<Node>();
            matches = permitted;
         
            if (values.Count != 0)
            {
                for (int i = 0; i < matches.Count; i++)
                    if (values[matches[i].Level] != "")
                        WriteValue(matches[i].Node, values[matches[i].Level], ref notCompleted);
            }

            var result = new List<Node>(); 
            for (int i = 0; i < matches.Count; i++)
                if (matches[i].Level == topLevel)
                {
                    nodes.Add(matches[i].Node);
                    if (!Settings.AdvancedPaths && !(matches[i].Node.Content.Children == null && delimited
                        || matches[i].Node.Content.Children != null && !delimited)) result.Add(matches[i].Node);
                }
            if (!Settings.AdvancedPaths && exists && result.Count == 0
                && error != Error.NotTraversable && error != Error.InsufficientPrivileges)
                error = Error.NotFound;
            if (exists) return Settings.AdvancedPaths ? nodes : result;
            result = new List<Node>();
            var count = matches.Count;

            for (int i = 0; i < nodes.Count; i++)
            {
                var node = nodes[i];
                var success = true;
                for (int j = topLevel + 1; j < names.Count; j++) 
                {
                    var name = String.Replace(String.Replace(names[j], Tokens.WildcardAny.ToString(), ""), Tokens.WildcardOne.ToString(), "");
                    if (name == "") { success = false; break; }
                    var content = new Content((!Settings.AdvancedPaths && j == names.Count - 1 && !delimited ? null : new List<Node>()), null);
                    var child = new Node(name, node, Permissions.Default(permissions.Owner), Attributes.Default(), content);
                    node.Content.Children.Add(child);
                    node = child; matches.Add(new NodeLevel(j, node));
                }
                if (success)
                {
                    result.Add(node);
                    created.Add(Get(node, Settings.AdvancedPaths || delimited));
                }
            }
            if (result.Count == 0 && error != Error.NotTraversable
                && error != Error.InsufficientPrivileges) error = Error.NotFound;

            if (values.Count != 0)
            {
                for (int i = count; i < matches.Count; i++)
                    if (values[matches[i].Level] != "")
                        WriteValue(matches[i].Node, values[matches[i].Level], ref notCompleted);
            }

            return result;
        }

        internal static string Get(Node node, bool delimited = true)
        {
            string path = delimited ? Separators.PathDelimiter.ToString() : "";
            while (node.Parent != null) { path = path.Insert(0, Separators.PathDelimiter + node.Name); node = node.Parent; }
            return path.Insert(0, Separators.IndicatorRoot.ToString());
        }

        private static bool WriteValue(Node node, string value, ref List<string> notCompleted)
        {
            int left = -1, right = -1, index = -1;
            bool trigger = false;
            for (int i = 1; i < value.Length - 1; i++)
            {
                if (value[i] == Tokens.VerbatimString && (!trigger || (value[i - 1] != Tokens.MirrorLeft
                    && value[i - 1] != Tokens.MirrorRight))) trigger = !trigger;

                if (!trigger)
                {
                    if (value[i] == Separators.PathParent) { left = i; break; }
                    if (value[i] == Separators.PathDelimiter) { right = i; break; }
                    if (value[i] == Tokens.Index) { index = i; break; }
                }
            }

            if ((left != -1 || right != -1 && index != -1) && value.Length == 3)
            { notCompleted.Add(Get(node, false) + value); return false; }

            if (left == -1 && right == -1 && index == -1)
            {
                node.Content.Data = String.Replace(value.Substring(1, value.Length - 2),
                    Tokens.VerbatimString.ToString(), "", true).Unmirror(Tokens.VerbatimString).Encode();
                return true;
            }

            int both = left == -1 ? right : left;
            var text = node.Content.Data.Decode();
     
            if (both == 1 || index == 1)
            {
                node.Content.Data = text.Insert(left == -1 && index == -1 ? text.Length : 0,
                    String.Replace(value.Substring(both == -1 ? index + 1 : both + 1, value.Length - 3),
                    Tokens.VerbatimString.ToString(), "", true).Unmirror(Tokens.VerbatimString)).Encode();
                return true;
            }

            if (both == value.Length - 2)
            {
                var temp = String.Replace(value.Substring(1, both - 1),
                    Tokens.VerbatimString.ToString(), "", true).Unmirror(Tokens.VerbatimString);
                var i = String.IndexOf(text, temp);
                if (i == -1) { notCompleted.Add(Get(node, false) + value); return false; }
                node.Content.Data = text.Remove(left == -1 ? i + temp.Length : 0,
                    left == -1 ? text.Length - (i + temp.Length) : i).Unmirror(Tokens.VerbatimString).Encode();
                return true;
            }

            if (both != -1)
            {
                var temp = String.Replace(value.Substring(1, both - 1),
                    Tokens.VerbatimString.ToString(), "", true).Unmirror(Tokens.VerbatimString);
                var i = String.IndexOf(text, temp);
                if (i == -1) { notCompleted.Add(Get(node, false) + value); return false; }
                node.Content.Data = text.Insert(i + (left == -1 ? temp.Length : 0),
                    String.Replace(value.Substring(both + 1, value.Length - both - 2),
                    Tokens.VerbatimString.ToString(), "", true).Unmirror(Tokens.VerbatimString)).Encode();
                return true;
            }

            if (index == value.Length - 2)
            {
                var temp = String.Replace(value.Substring(1, index - 1),
                    Tokens.VerbatimString.ToString(), "", true).Unmirror(Tokens.VerbatimString);
                if (String.IndexOf(text, temp) == -1) { notCompleted.Add(Get(node, false) + value); return false; }
                node.Content.Data = String.Replace(text, temp, "").Encode();
                return true;
            }

            var finalTemp = String.Replace(value.Substring(1, index - 1),
                Tokens.VerbatimString.ToString(), "", true).Unmirror(Tokens.VerbatimString);
            var finalIndex = String.IndexOf(text, finalTemp);

            bool indexUsed = true;
            int position = -1;
            try { position = Convert.ToInt32(finalTemp); } catch { indexUsed = false; }

            if ((!indexUsed && finalIndex == -1) || (indexUsed && position < 0)
                || (indexUsed && position > text.Length))
            { notCompleted.Add(Get(node, false) + value); return false; }

            if (indexUsed) node.Content.Data = text.Insert(position,
                String.Replace(value.Substring(index + 1, value.Length - index - 2),
                Tokens.VerbatimString.ToString(), "", true).Unmirror(Tokens.VerbatimString)).Encode();
            else node.Content.Data = String.Replace(text, finalTemp,
                String.Replace(value.Substring(index + 1, value.Length - index - 2),
                Tokens.VerbatimString.ToString(), "", true).Unmirror(Tokens.VerbatimString)).Encode();
            return true;
        }

        private static List<Node> Evaluate(List<string> path, int startIndex, Node parent, ref List<NodeLevel> result, bool quick = false, bool includeHidden = true)
        {
            var matches = parent.FindChildren(path[startIndex], includeHidden);
            var temp = new List<Node>();
            if (matches.Count != 0)
            {
                if (!quick) for (int j = 0; j < matches.Count; j++) result.Add(new NodeLevel(startIndex, matches[j]));
                if (startIndex == path.Count - 1) return matches;
                for (int i = 0; i < matches.Count; i++)
                {
                    var next = Evaluate(path, startIndex + 1, matches[i], ref result, quick, includeHidden);
                    if (next.Count != 0) { temp = next; if (quick) break; }
                }
            }
            return temp;
        }

        private static List<Node> Evaluate(List<string> path, int startIndex, Node parent, bool includeHidden = true)
        {
            var matches = parent.FindChildren(path[startIndex], includeHidden);
            var temp = new List<Node>();
            if (matches.Count != 0)
            {
                if (startIndex == path.Count - 1) return matches;
                for (int i = 0; i < matches.Count; i++)
                {
                    var next = Evaluate(path, startIndex + 1, matches[i], includeHidden);
                    if (next.Count != 0) { temp = next; break; }
                }
            }
            return temp;
        }
    }
}
